#include<stdio.h>
#include<string.h>
#include<assert.h>
#include "sock_utils.h"
#include "p2p-engine.h"
#include "regex_utils.h"
#include "p2p-peer.h"
#include "p2p-config.h"
#include "my_log.h"

int main(int argc, char *argv[])
{
    fd_set read_fds, master;
    int bytes_recvd, listener, i, j, newfd, return_val, fdmax, remote_socket_count = 0, br;
    const int protocol_no = 0, backlog = 10;
    char recv_buf[MAXBUFLEN], *ip_port;
    struct sockaddr_in client_addr;
    socklen_t addrlen;
    socket_t local_socket, *remote_socket;
    subexp_t subexp; 
    
    peer_list_t peer_list;
    peer_t *peer;
    peer_list.count = 0;


    if (argc >= 3) {
        strcpy(local_socket.ip_addr, argv[1]);
        local_socket.port = atoi(argv[2]);
        if (argc == 4) {

            /*  Parse the IP addresses and ports of peers given at command line. 
                They are given in the following format:
                <IP Address:Port>,<IP Address:Port>, and so on
            */
            remote_socket = (socket_t *)malloc((count_chars(argv[3], ',') + 1) * sizeof(socket_t));
            ip_port = strtok(argv[3], ",");
            while (ip_port != NULL) {
                if (!regex(ip_port, "([0-9.]+):([0-9]+)", 3, &subexp, REG_EXTENDED)) {
                    strcpy(remote_socket[remote_socket_count].ip_addr, subexp.match[1]);
                    remote_socket[remote_socket_count].port = atoi(subexp.match[2]);
                    remote_socket_count++;
                }
                ip_port = strtok(NULL, ",");
            }
        }
    } else { 
        strcpy(local_socket.ip_addr, "192.168.1.10");
        local_socket.port = 7000;
    }

    printf("[p2p-client] Initializing...\n");
    
    if ((listener = create_tcp_socket(AF_INET, protocol_no, &local_socket, NULL, backlog)) == -1)
        exit(1);

	printf("[p2p-client] Done\n");

    FD_ZERO(&master);
    FD_ZERO(&read_fds);
    
    FD_SET(listener, &master);
    fdmax = listener; 

    return_val = init();
    if (return_val == -1) {
        printf("[p2p-client] Error Initializing\n");
        exit(1);
    } else if (return_val) {
        if (remote_socket_count) {
            if (connect_to_peers(&local_socket, remote_socket, &master, &fdmax, remote_socket_count, &peer_list) == -1) {
                printf("[p2p-client] Connecting to peers failed\n");
                return -1;
            }
            if (send_handshake_to_peers(&peer_list) == -1) {
                printf("[p2p-client] Connecting to peers failed\n");
                return -1;
            }
        } else {
            printf("[p2p-client] No peers to connect to\n");
        }
    }
    
    while (1) {
        read_fds = master; 
        if (select(fdmax + 1, &read_fds, NULL, NULL, NULL) == -1) {
            printf("Error on select system call\n");
            exit(1);
        }
        for(i = 0; i <= fdmax; i++) {
            if (FD_ISSET(i, &read_fds)) { 
                if (i == listener) {
                    addrlen = sizeof(client_addr);
                    if ((newfd = accept(listener, (struct sockaddr *)&client_addr, 
                        &addrlen)) == -1) {
                        printf("Error on accept system call\n");
                    } else {
                        FD_SET(newfd, &master); // add to master set
                        if (newfd > fdmax) 
                            fdmax = newfd;
                        printf("[p2p-client] New connection from %s on "
                        "socket %d\n", inet_ntoa(client_addr.sin_addr), newfd);
                        if (!peer_list.count) {
                            peer_list.head = init_peer(newfd);
                            peer_list.tail = peer_list.head;
                        } else {
                            peer_list.tail = add_peer(peer_list.tail, newfd);
                        }
                        peer_list.count++;
                    }
                } else {
                    if ((bytes_recvd = recv(i, recv_buf, sizeof(recv_buf), 0)) <= 0) {
                        // got error or connection closed by client
                        if (bytes_recvd == 0) {
                            printf("[p2p-client] Peer with sockfd:%d closed the connection\n", i);
                        } else {
                            perror("recv");
                        }
                        cleanup(&peer_list, i);
                        delete_socket(i, &master);
                    } else {
                        /*printf("recv_buffer:");
                        for (j = 0; j < bytes_recvd; j++)
                            printf("%x", recv_buf[j]);
                        printf("\n");*/
                        // Close the connection if handle_pkt reports failure
                        br = 0;
                        while (br < bytes_recvd) {
                            br = handle_msg(i, recv_buf + br, bytes_recvd -= br, &peer_list);
                            if (br == -1) {
                                printf("[p2p-client] handle_msg failed\n");
                                cleanup(&peer_list, i);
                                delete_socket(i, &master);
                            } 
                        }
                        /*else if (return_val < bytes_recvd) {
                            printf("[p2p-client] Closing connection to peer\n");
                            cleanup(&peer_list, i);
                            delete_socket(i, &master);
                        }*/
                    }
                }
            }
        }
    }
    return 0;
}
